package org.vacoor.nothing.common.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.vacoor.nothing.common.codec.Base64;

import javax.net.ssl.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;

/**
 * 简单的Http 请求工具类
 *
 * @author vacoor
 */
public abstract class HttpUtils {
    private static final Logger LOG = LoggerFactory.getLogger(HttpUtils.class);
    private static final String POST_METHOD = "POST";
    private static final String ENCODING = "UTF-8";

    public static boolean postOnly(String serverUrl, Map<String, String> data, int timeout) {
        InputStream is = null;
        try {
            is = post(serverUrl, data, timeout);
            return true;
        } catch (Throwable ignore) {
            // ignore
        } finally {
            if (null != is) {
                try {
                    is.close();
                } catch (IOException e) {
                    // ignore
                }
            }
        }
        return false;
    }

    public static InputStream post(String serverUrl, Map<String, ?> data, int timeout) throws IOException {
        OutputStreamWriter writer = null;
        try {
            HttpURLConnection conn = createConnection(serverUrl);
            conn.setRequestMethod(POST_METHOD);
            conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
            String userInfo = conn.getURL().getUserInfo();
            if (null != userInfo) {
                conn.setRequestProperty("Authorization", "Basic " + Base64.encodeToString(Bytes.toBytes(userInfo)));
            }
            conn.setConnectTimeout(timeout);
            /*
            conn.setDoOutput(true);
            writer = new OutputStreamWriter(conn.getOutputStream());

            if (null != data) {
                boolean first = true;
                for (Map.Entry<String, ?> entry : data.entrySet()) {
                    if (!first) {
                        writer.write('&');
                    }
                    first = false;
                    writer.write(URLEncoder.encode(entry.getKey(), ENCODING));
                    writer.write('=');
                    Object value = entry.getValue();
                    writer.write(null == value ? "" : URLEncoder.encode(String.valueOf(value), ENCODING));
                }
            }
            writer.flush();
            */
            sendPostData(conn, data);

            return conn.getInputStream();
        } catch (NoSuchAlgorithmException e) {
            LOG.warn("", e);
            throw Throwables.unchecked(e);
        } catch (KeyManagementException e) {
            LOG.warn("", e);
            throw Throwables.unchecked(e);
        } finally {
            if (null != writer) {
                try {
                    writer.close();
                } catch (IOException ignore) {/* ignore */}
            }
        }
    }

    public static InputStream get(String serverUrl, Map<String, ?> data, int timeout) {
        try {
            if (null != data) {
                StringBuilder buff = new StringBuilder();
                for (Map.Entry<String, ?> entry : data.entrySet()) {
                    buff.append('&');
                    buff.append(URLEncoder.encode(entry.getKey(), ENCODING));
                    buff.append('=');
                    Object value = entry.getValue();
                    buff.append(null == value ? "" : URLEncoder.encode(String.valueOf(value), ENCODING));
                }
                //
                serverUrl += 0 > serverUrl.indexOf('?') ? buff.replace(0, 1, "?") : buff;
            }
            LOG.debug("do request url: {}", serverUrl);
            HttpURLConnection conn = createConnection(serverUrl);
            String userInfo = conn.getURL().getUserInfo();
            if (null != userInfo) {
                conn.setRequestProperty("Authorization", "Basic " + Base64.encodeToString(Bytes.toBytes(userInfo)));
            }
            conn.setDoOutput(true);
            conn.setConnectTimeout(timeout);

            return conn.getInputStream();
        } catch (KeyManagementException e) {
            LOG.warn("", e);
            throw Throwables.unchecked(e);
        } catch (NoSuchAlgorithmException e) {
            LOG.warn("", e);
            throw Throwables.unchecked(e);
        } catch (IOException e) {
            LOG.warn("", e);
            throw Throwables.unchecked(e);
        }
    }

    public static HttpURLConnection createConnection(String serverUrl) throws NoSuchAlgorithmException, KeyManagementException, IOException {
        URL url = new URL(serverUrl);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();

        if ("https".equals(url.getProtocol())) {
            LOG.debug("init ssl context for {}", url);
            // 初始化 SSL 上下文
            SSLContext sslContext = SSLContext.getInstance("SSL");
            // 类似12306 会使用证书, 这里初始化为不是用密钥库, 信任任何证书
            sslContext.init(new KeyManager[]{}, new TrustManager[]{new TrustAnyTrustManager()}, new SecureRandom());
            SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

            ((HttpsURLConnection) conn).setSSLSocketFactory(sslSocketFactory);
        }
        return conn;
    }

    public static void sendPostData(HttpURLConnection conn, Map<String, ?> data) throws IOException {
        conn.setDoOutput(true);
        OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream());

        if (null != data) {
            boolean first = true;
            for (Map.Entry<String, ?> entry : data.entrySet()) {
                if (!first) {
                    writer.write('&');
                }
                first = false;
                writer.write(URLEncoder.encode(entry.getKey(), ENCODING));
                writer.write('=');
                Object value = entry.getValue();
                writer.write(null == value ? "" : URLEncoder.encode(String.valueOf(value), ENCODING));
            }
        }
        writer.flush();
    }

    /**
     * 信任任何证书的信任库管理器
     */
    private static class TrustAnyTrustManager implements X509TrustManager {

        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[]{};
        }
    }

    private HttpUtils() {
    }
}
